// Header files and standard libraries
#include <math.h>
#include <iostream>
#include <fstream>
#include <vector>
#ifdef __WINDOWS__
	#include <string>
	#include <GL/glut.h>
#else
	#include <string.h>
	#include <GL/freeglut.h>
#endif

using std::string;
using std::ifstream;
using std::cout;
using std::endl;
using std::vector;


// Define window
#define WINDOW_XS 500
#define WINDOW_YS 500
#define WINDOW_NAME "3D Image Viewer"
#define PI 3.14159265

// Define point structure
typedef struct pt
{
	GLdouble x, y, z;
}MyPoint;

vector<MyPoint> vertices;
bool autoRotate = false;
double alpha = 0.0;
double center_x, center_y, center_z, rad, axis_a, axis_b, axis_c;
double near_x = -100.0, far_x = 100.0, near_y = -100.0, far_y = 100.0, near_z = -500.0, far_z = 500.0;

// Function prototypes
void display_func(void);
void keyboard_func(unsigned char c, int x, int y);
void mouse_func(int button, int state, int x, int y);
void reshape_handler(int width, int height);
void init_setup(int width, int height, char *windowName);
bool readFile(string fileName);

// Begin main function
int main(int argc, char **argv)
{
	string fileName;
	glutInit(&argc, argv);

	init_setup(WINDOW_XS, WINDOW_YS, (char *)WINDOW_NAME);
	
	if(argc >= 2)
		fileName = argv[1];
	else
		fileName = "inputPixels";
	
	if(!readFile(fileName))
	{
		cout << "Error reading data from file: " << fileName << endl;
		return 1;
	}

	glutKeyboardFunc(keyboard_func);
	glutDisplayFunc(display_func);
	glutMouseFunc(mouse_func);
	glutIdleFunc(display_func);
	glutMainLoop();

	return 1;
}// End main function

// Begin display function
void display_func(void)
{

	glMatrixMode(GL_MODELVIEW);
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


	glLoadIdentity();

	glEnable(GL_DEPTH_TEST);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	gluLookAt(0.0, 0.0, 0.0, center_x, center_y, center_z, 0.0, 1.0, 0.0);
	glTranslatef(center_x, center_y, center_z);
	glRotatef(alpha,0.0f,1.0f,0.0f);
	glTranslatef(-center_x, -center_y, -center_z);
	
	
	glColor3f(1.0,1.0,1.0);
	glBegin(GL_POINTS);
	for(unsigned int i = 0; i < vertices.size(); i++)
	{
		glVertex3f(vertices[i].x,vertices[i].y,vertices[i].z);
	}
	glEnd();
	
	glLoadIdentity();

	if(autoRotate)
	{
		alpha += 0.5;

		if (alpha >= 360.0)
			alpha = 0.0;
	}

	glutSwapBuffers();
}// End display function

// Begin keyboard function
void keyboard_func(unsigned char c, int x, int y)
{
	if(c == 'r')
	{
		if (alpha < 360.0)
			alpha += 0.5;
	}
	if(c == 't')
		if (alpha > 0.0)
			alpha -= 0.5;
	if(c == 'a')
		autoRotate = !autoRotate;

	if(c == 'z')
	{
		near_x *= 0.75;
		far_x *= 0.75;
		near_y *= 0.75;
		far_y *= 0.75;
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(near_x, far_x, near_y, far_y, near_z, far_z);
	}
	if(c == 'Z')
	{
		near_x *= 1.25;
		far_x *= 1.25;
		near_y *= 1.25;
		far_y *= 1.25;
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(near_x, far_x, near_y, far_y, near_z, far_z);
	}	
	glutPostRedisplay();
}// End keyboard function

// Begin mouse function
void mouse_func(int button, int state, int x, int y)
{
	if( (button == GLUT_LEFT_BUTTON)) //&&
		//(state == GLUT_DOWN) )
	{

	}


	if( (button == GLUT_RIGHT_BUTTON) &&
		(state == GLUT_DOWN) )
	{

	}

	glutPostRedisplay();

}// End mouse function

void reshape_handler(int width, int height)
{
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(near_x, far_x, near_y, far_y, near_z, far_z);
}

void init_setup(int width, int height, char *windowName)
{
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize(width, height);
	glutInitWindowPosition(5, 5);
	glutCreateWindow(windowName);
	glutReshapeFunc(reshape_handler);
}

bool readFile(string fileName)
{
	ifstream inFile(fileName.c_str());
	if(!inFile)
		return false;
	
	
	double temp;
	inFile >> temp;
	if(temp == 4.0)
	{
		inFile >> center_x >> center_y >> center_z >> rad;
		//isSphere = true;
	}
	else if(temp == 6.0)
	{
		inFile >> center_x >> center_y >> center_z >> axis_a >> axis_b >> axis_c;
		//isSphere = false;
	}
	else
	{
		cout << "Invalid shape. Exiting..." << endl;
		return false;
	}
	
	while(!inFile.eof())
	{
		MyPoint tempPt;
		inFile >> temp;
		tempPt.x = temp;

		inFile >> temp;
		tempPt.y = temp;

		inFile >> temp;
		tempPt.z = temp;
		
		vertices.push_back(tempPt);
	}

	return true;
}
